﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace YZH.Audio
{
    public enum WaveFormatType
    {
        Pcm = 1,
        Float = 3
    }

    [StructLayout(LayoutKind.Sequential)]
    public class WaveFormat
    {
        public short wFormatTag;
        public short nChannels;
        public int nSamplesPerSec;
        public int nAvgBytesPerSec;
        public short nBlockAlign;
        public short wBitsPerSample;
        public short cbSize;

        public WaveFormat(int rate, int bits, int channels)
        {
            wFormatTag = (short)WaveFormatType.Pcm;
            nChannels = (short)channels;
            nSamplesPerSec = rate;
            wBitsPerSample = (short)bits;
            cbSize = 0;
            nBlockAlign = (short)(channels * (bits / 8));
            nAvgBytesPerSec = nSamplesPerSec * nBlockAlign;
        }
    }
    internal enum WaveInOpenFlags : uint
    {
        CALLBACK_NULL = 0,
        CALLBACK_FUNCTION = 0x30000,
        CALLBACK_EVENT = 0x50000,
        CALLBACK_WINDOW = 0x10000,
        CALLBACK_THREAD = 0x20000,
        WAVE_FORMAT_QUERY = 1,
        WAVE_MAPPED = 4,
        WAVE_FORMAT_DIRECT = 8
    }
    public enum PlaySoundFlags : uint
    {
        SND_SYNC = 0x0,            // play synchronously (default)
        SND_ASYNC = 0x1,            // play asynchronously
        SND_NODEFAULT = 0x2,        // silence (!default) if sound not found
        SND_MEMORY = 0x4,            // pszSound points to a memory file
        SND_LOOP = 0x8,            // loop the sound until next sndPlaySound
        SND_NOSTOP = 0x10,            // don't stop any currently playing sound
        SND_NOWAIT = 0x2000,        // don't wait if the driver is busy
        SND_ALIAS = 0x10000,        // name is a registry alias
        SND_ALIAS_ID = 0x110000,        // alias is a predefined ID
        SND_FILENAME = 0x20000,        // name is file name
        SND_RESOURCE = 0x40004        // name is resource name or atom
    };

    [StructLayout(LayoutKind.Sequential)]
    public struct WaveHdr
    {

        public IntPtr lpData; // pointer to locked data buffer
        public int dwBufferLength; // length of data buffer
        public int dwBytesRecorded; // used for input only
        public IntPtr dwUser; // for client's use
        public int dwFlags; // assorted flags (see defines)
        public int dwLoops; // loop control counter
        public IntPtr lpNext; // PWaveHdr, reserved for driver
        public int reserved; // reserved for driver
    }
    internal class WaveAPI
    {
        public const int MMSYSERR_NOERROR = 0; // no error

        public const int MM_WOM_OPEN = 0x3BB;
        public const int MM_WOM_CLOSE = 0x3BC;
        public const int MM_WOM_DONE = 0x3BD;

        public const int MM_WIM_OPEN = 0x3BE;
        public const int MM_WIM_CLOSE = 0x3BF;
        public const int MM_WIM_DATA = 0x3C0;
        
        public const int TIME_MS = 0x0001;  // time in milliseconds 
        public const int TIME_SAMPLES = 0x0002;  // number of wave samples 
        public const int TIME_BYTES = 0x0004;  // current byte offset 

        public const int WAVE_MAPPER = -1;

        private const string mmdll = "winmm.dll";

        // callbacks
        public delegate void WaveDelegate(IntPtr hdrvr, int uMsg, int dwUser, ref WaveHdr wavhdr, int dwParam2);

        // structs 
        
        /*
        // WaveOut calls
        [DllImport(mmdll)]
        public static extern int waveOutGetNumDevs();
        [DllImport(mmdll)]
        public static extern int waveOutPrepareHeader(IntPtr hWaveOut, ref WaveHdr lpWaveOutHdr, int uSize);
        [DllImport(mmdll)]
        public static extern int waveOutUnprepareHeader(IntPtr hWaveOut, ref WaveHdr lpWaveOutHdr, int uSize);
        [DllImport(mmdll)]
        public static extern int waveOutWrite(IntPtr hWaveOut, ref WaveHdr lpWaveOutHdr, int uSize);
        [DllImport(mmdll)]
        public static extern int waveOutOpen(out IntPtr hWaveOut, int uDeviceID, WaveFormat lpFormat, WaveDelegate dwCallback, int dwInstance, int dwFlags);
        [DllImport(mmdll)]
        public static extern int waveOutReset(IntPtr hWaveOut);
        [DllImport(mmdll)]
        public static extern int waveOutClose(IntPtr hWaveOut);
        [DllImport(mmdll)]
        public static extern int waveOutPause(IntPtr hWaveOut);
        [DllImport(mmdll)]
        public static extern int waveOutRestart(IntPtr hWaveOut);
        [DllImport(mmdll)]
        public static extern int waveOutGetPosition(IntPtr hWaveOut, out int lpInfo, int uSize);
        [DllImport(mmdll)]
        public static extern int waveOutSetVolume(IntPtr hWaveOut, int dwVolume);
        [DllImport(mmdll)]
        public static extern int waveOutGetVolume(IntPtr hWaveOut, out int dwVolume);
        */

        [DllImport(mmdll, SetLastError = true)]
        public static extern int PlaySound(string szSound, IntPtr hModule, int flags);

        [DllImport(mmdll, SetLastError = true)]
        public static extern int PlaySound(byte[] szSound, IntPtr hModule, int flags);
        // WaveIn calls
        [DllImport(mmdll, CallingConvention = CallingConvention.StdCall)]
        public static extern int waveInGetNumDevs();
        [DllImport(mmdll, CallingConvention = CallingConvention.StdCall)]
        public static extern int waveInAddBuffer(IntPtr hwi, ref WaveHdr pwh, int cbwh);
        [DllImport(mmdll, CallingConvention = CallingConvention.StdCall)]
        public static extern int waveInClose(IntPtr hwi);
        [DllImport(mmdll, CallingConvention = CallingConvention.StdCall)]
        public static extern int waveInOpen(out IntPtr phwi, int uDeviceID, WaveFormat lpFormat, WaveDelegate dwCallback, int dwInstance, int dwFlags);
        [DllImport(mmdll, CallingConvention = CallingConvention.StdCall)]
        public static extern int waveInPrepareHeader(IntPtr hWaveIn, ref WaveHdr lpWaveInHdr, int uSize);
        [DllImport(mmdll, CallingConvention = CallingConvention.StdCall)]
        public static extern int waveInUnprepareHeader(IntPtr hWaveIn, ref WaveHdr lpWaveInHdr, int uSize);
        [DllImport(mmdll, CallingConvention = CallingConvention.StdCall)]
        public static extern int waveInReset(IntPtr hwi);
        [DllImport(mmdll, CallingConvention = CallingConvention.StdCall)]
        public static extern int waveInStart(IntPtr hwi);
        [DllImport(mmdll, CallingConvention = CallingConvention.StdCall)]
        public static extern int waveInStop(IntPtr hwi);
    }
}
